#ifndef __INCvalidatorh
#define __INCvalidatorh
#include <vector>
#include <string>
#include <algorithm>
#include <functional>
#include <stdexcept>
#include <type.h>

namespace parser
{
    using ValidatorFunc = bool (*)(const char *);

    template <typename T>
    ValidatorFunc oneof(const T &a, const T &b)
    {
        static const T val_a = a;
        static const T val_b = b;

        return [](const char *value) -> bool
        {
            if (!value)
            {
                return false;
            }

            T input = T(value);

            return (input == val_a) || (input == val_b);
        };
    }

    template <typename T>
    ValidatorFunc range(T min_val, T max_val)
    {
        static const T min = min_val;
        static const T max = max_val;

        static_assert(std::is_arithmetic<T>::value,
                      "range validator only supports arithmetic types");

        return [](const char *value) -> bool
        {
            if (!value || (*value == EOFS))
            {
                return false;
            }

            try
            {
                T num;
                if constexpr (std::is_integral<T>::value)
                {
                    num = static_cast<T>(std::stoll(value));
                }
                else if constexpr (std::is_floating_point<T>::value)
                {
                    num = static_cast<T>(std::stold(value));
                }

                return (num >= min) && (num <= max);
            }
            catch (const std::invalid_argument &)
            {
                return false;
            }
            catch (const std::out_of_range &)
            {
                return false;
            }
        };
    }

    template <typename EnumType>
    ValidatorFunc enum_validator(
        std::function<bool(const std::string &, EnumType &)> str_to_enum,
        const std::vector<EnumType> &allowed_values)
    {
        static std::vector<EnumType> allowed = allowed_values;
        static auto converter = str_to_enum;

        return [](const char *value) -> bool
        {
            if (!value || (*value == EOFS))
            {
                return false;
            }

            EnumType enum_val;

            if (!converter(std::string(value), enum_val))
            {
                return false;
            }

            return std::find(allowed.begin(), allowed.end(), enum_val) != allowed.end();
        };
    }
} // namespace parser
#endif